package cs342.project1.mathNotebook.data;

import java.io.*;
import java.util.*;
import java.math.*;
import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;

	/**
        * @author Thomas Naps
	* @author Dexter Hoban
	*/

public class Encryption {
    
	/**
	* Encrypts the file with the specified password.
	*
	* @param filename The name of the file you with to encrypt.
	* @param password The password you want to encrypt the file with;
	* @param outputFilename The location you wish to save the data file.
	*/

    public static void encrypt(String filename, String password, String outputFilename) {
        /*
         *  Naps StringEncryptor.java class
         */
        try {
            InputStream in = new FileInputStream(filename);
            FileOutputStream fout = new FileOutputStream(outputFilename);

            // Create a key.
            byte[] desKeyData = password.getBytes();
            DESKeySpec desKeySpec = new DESKeySpec(desKeyData);
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey desKey = keyFactory.generateSecret(desKeySpec);

            // Use Data Encryption Standard cipher
            Cipher des = Cipher.getInstance("des");
            des.init(Cipher.ENCRYPT_MODE, desKey);

            DataOutputStream dout = new DataOutputStream(fout);

            byte[] input = new byte[64];
            while (true) {
                int bytesRead = in.read(input);
                if (bytesRead == -1) break;
                byte[] output = des.update(input, 0, bytesRead);
                if (output != null) dout.write(output);
            }
            byte[] output = des.doFinal();
            if (output != null) dout.write(output);
            in.close();
            dout.flush();
            dout.close();
        }
        catch (InvalidKeySpecException e) {
            System.err.println(e);
            e.printStackTrace();
        }
        catch (InvalidKeyException e) {
            System.err.println(e);
            e.printStackTrace();
        }
        catch (NoSuchAlgorithmException e) {
            System.err.println(e);
            e.printStackTrace();
        }
        catch (NoSuchPaddingException e) {
            System.err.println(e);
            e.printStackTrace();
        }
        catch (BadPaddingException e) {
            System.err.println(e);
            e.printStackTrace();
        }
        catch (IllegalBlockSizeException e) {
            System.err.println(e);
            e.printStackTrace();
        }
        catch (IOException e) {
            System.err.println(e);
            e.printStackTrace();
        }
    }
    
	/**
	* Decrypts the file with the specified password.
	*
	* @param filename The name of the file you with to decrypt.
	* @param password The password you want to decrypt the file with;
	* @param outputFilename The location you wish to save the gz file.
	*/

    public static void decrypt(String filename, String password, String outputFilename) {
        /*
         *  Naps StringDecryptor.java class
         */
        String decrypted_string = null;
        try {
            FileInputStream fin = new FileInputStream(filename);
            FileOutputStream outStream = new FileOutputStream(outputFilename);

            byte [] desKeyData = password.getBytes ();
            DESKeySpec desKeySpec = new DESKeySpec( desKeyData );
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance ( "DES" );
            SecretKey desKey = keyFactory.generateSecret( desKeySpec );
            
            Cipher des = Cipher.getInstance ( "des" );
            des.init( Cipher.DECRYPT_MODE , desKey );
            decrypted_string = new String ("");
            
            byte [] input = new byte [64];
            while ( true ) {
                    int bytesRead = fin.read(input);
                    if ( bytesRead == -1) break;
                        byte[] output = des.update( input , 0, bytesRead );
                        if ( output != null ) {
                        	outStream.write(output);
                        }
                        	
                        	//decrypted_string = decrypted_string.concat (new String(output));
            }
            byte[] output = des.doFinal ();
                        
            if( output != null ) decrypted_string = decrypted_string.concat ( new String (output));
            fin.close ();
        }
        catch ( InvalidKeySpecException e ) {
            System.err.println(e);
            e.printStackTrace();
        }
        catch ( InvalidKeyException e ) {
            System.err.println(e);
            e.printStackTrace();
        }
        catch ( NoSuchAlgorithmException e ) {
            System.err.println(e);
            e.printStackTrace();
        }
        catch ( NoSuchPaddingException e ) {
            System.err.println(e);
            e.printStackTrace();
        }
        catch ( BadPaddingException e ) {
            System.err.println(e);
            e.printStackTrace();
        }
        catch ( IllegalBlockSizeException e) {
            System.err.println(e);
            e.printStackTrace();
        }
        catch ( IOException e ) {
            System.err.println(e);
            e.printStackTrace();
        }
    }

	/**
	* RSA Encrypts the specified string with the keyfile.
	*
	* @param DESEncKey The DES key that you want to Encrypt.
	* @param keyFile The key file you will use to encrypt this key.
	* @param outputFilename The location you wish to save the encrypted file.
	*/
    
    public static void RSAencrypt(String DESEncKey, String keyFile, String outputFilename) {
        /*
         *  Naps RSAStringEncryptorFromBigInt.java class
         */
        try {
            InputStream in = new ByteArrayInputStream(DESEncKey.getBytes() );
            PrintWriter fout = new PrintWriter(outputFilename);

            // Get the public key.
            KeyFactory keyFactory = KeyFactory.getInstance("rsa");
            FileInputStream fis = new FileInputStream(keyFile);
            int kl = fis.available();
            byte[] kb = new byte[kl];
            fis.read(kb);
            fis.close();
            PublicKey rsa_pub_key = null;
            X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(kb);
            rsa_pub_key = keyFactory.generatePublic(pubKeySpec);

            // Use RSA cipher
            Cipher rsa = Cipher.getInstance("rsa");
            rsa.init(Cipher.ENCRYPT_MODE, rsa_pub_key);
      
            ArrayList enc_bytes = new ArrayList();
      
            byte[] input = new byte[64];
            while (true) {
                int bytesRead = in.read(input);
                if (bytesRead == -1) break;
                byte[] output = rsa.update(input, 0, bytesRead);
                if (output != null) 
                    for (int i = 0; i < output.length; i++) enc_bytes.add(new Byte(output[i]));  
            }
      
            byte[] output = rsa.doFinal();
            if (output != null) 
                for (int i = 0; i < output.length; i++) enc_bytes.add(new Byte(output[i]));  
            in.close();
            Object enc_data_wrap [] =  enc_bytes.toArray();
            byte enc_data [] = new byte [enc_data_wrap.length];
            for (int i = 0; i < enc_data_wrap.length; i++) 
            	enc_data[i] = ((Byte)enc_data_wrap[i]).byteValue();
            
            BigInteger bi = new BigInteger(enc_data);
            fout.print(bi.toString());
            fout.close();
        }
        catch (InvalidKeySpecException e) {
            System.err.println(e);
            e.printStackTrace();
        }
        catch (InvalidKeyException e) {
            System.err.println(e);
            e.printStackTrace();
        }
        catch (NoSuchAlgorithmException e) {
            System.err.println(e);
            e.printStackTrace();
        }
        catch (NoSuchPaddingException e) {
            System.err.println(e);
            e.printStackTrace();
        }
        catch (BadPaddingException e) {
            System.err.println(e);
            e.printStackTrace();
        }
        catch (IllegalBlockSizeException e) {
            System.err.println(e);
            e.printStackTrace();
        }
        catch (IOException e) {
            System.err.println(e);
            e.printStackTrace();
        }
    }

	/**
	* Decrypts the RSA file using the key.
	*
	* @param DESEncKey The the name of the file to decode.
	* @param keyfile The keyfile to use to decode;
	* @return A String representing the DES key.
	*/

    public static String RSAdecrypt(String DESEncKey, String keyFile) {
        /*
         *  Naps RSAStringEncryptorFromBigInt.java class
         */
        try {
            BufferedReader in = new BufferedReader(new FileReader(DESEncKey));
            BigInteger bi = new BigInteger(in.readLine());
            InputStream bin = new ByteArrayInputStream(bi.toByteArray() );
            
            // Get the public key.
            KeyFactory keyFactory = KeyFactory.getInstance("rsa");
            FileInputStream fis = new FileInputStream(keyFile);
            int kl = fis.available();
            byte[] kb = new byte[kl];
            fis.read(kb);
            fis.close();
            PrivateKey rsa_pri_key = null;
            PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec(kb);
            rsa_pri_key = keyFactory.generatePrivate(priKeySpec);
            
            // Use RSA cipher
            Cipher rsa = Cipher.getInstance("rsa");
            rsa.init(Cipher.DECRYPT_MODE, rsa_pri_key);
      
            String decrypted_string = new String("");

            byte[] input = new byte[64];
            while (true) {
                int bytesRead = bin.read(input);
                if (bytesRead == -1) break;
                byte[] output = rsa.update(input, 0, bytesRead);
                if (output != null) decrypted_string = decrypted_string.concat(new String(output)) ;
            }
      
            byte[] output = rsa.doFinal();
            if (output != null) decrypted_string = decrypted_string.concat(new String(output));
            bin.close();
            return decrypted_string;
        }
        catch (InvalidKeySpecException e) {
            System.err.println(e);
            e.printStackTrace();
        }
        catch (InvalidKeyException e) {
            System.err.println(e);
            e.printStackTrace();
        }
        catch (NoSuchAlgorithmException e) {
            System.err.println(e);
            e.printStackTrace();
        }
        catch (NoSuchPaddingException e) {
            System.err.println(e);
            e.printStackTrace();
        }
        catch (BadPaddingException e) {
            System.err.println(e);
            e.printStackTrace();
        }
        catch (IllegalBlockSizeException e) {
            System.err.println(e);
            e.printStackTrace();
        }
        catch (IOException e) {
            System.err.println(e);
            e.printStackTrace();
        }
        return null;
    }
}